use std::{borrow::Borrow, sync::Arc};

use crate::{
    errors::cons_error,
    mdb::{MClient, ModelEx, MDB},
    models::{
        club_activity::{ClubActivityJoinModel, ClubActivityJoinedUser},
        club_form::{ClubFormModel, ClubFormType},
        user::SimpleUserModel,
        user_application::{
            ClubFormStatusHistory, UserApplicationModel, UserApplicationSourceForm,
        },
        ClubFormStatusFlow, ClubFormStatusFlowType,
    },
    utils::{
        db_utils::{build_filter, json_to_bson, json_to_document, COMMON_NO_REGEXP},
        json::valid_form_data,
    },
    AppError, AppResult, Pagination, PaginationQuery,
};

use super::{club_service::ClubService, user_service::UserService};
use actix_http::StatusCode;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use wither::{
    bson::{doc, oid::ObjectId, to_bson, DateTime, Document},
    mongodb::options::{FindOneAndUpdateOptions, ReturnDocument, UpdateModifications},
    Model,
};

mod serde_default {}

#[derive(Debug, Clone)]
pub struct FormService {
    pub club_service: Arc<ClubService>,
    pub user_service: Arc<UserService>,
    pub mdb: MDB,
    pub m_client: MClient,
}

impl FormService {
    pub fn new(
        club_service: Arc<ClubService>,
        user_service: Arc<UserService>,
        mdb: MDB,
        m_client: MClient,
    ) -> Self {
        Self {
            club_service,
            user_service,
            mdb,
            m_client,
        }
    }
    pub fn valid_form_rule(value:Value) -> AppResult<()> {
        let mut scope = valico::json_schema::Scope::new();
        scope.compile_and_return(value,false)?;
        Ok(())
    }
    pub async fn create_form_to_club(&self, req: CreateFormToClubReq) -> AppResult<ClubFormModel> {
        if let Some(ref rule) = req.rule {
            Self::valid_form_rule(rule.clone())?;
        }

        let mut model = ClubFormModel::from(req);
        model.status_flow = Some(ClubFormStatusFlow::default_club_form_status_flow());
        if model.form_type == ClubFormType::Join {
            self.reset_join_form(model.club_id).await?;
        }
        model.save(&*self.mdb.clone(), None).await?;
        Ok(model)
    }
    /// 充值社团中的所有的纳新表单
    pub async fn reset_join_form<T: Into<ObjectId>>(&self, club_id: T) -> AppResult<()> {
        let club_id: ObjectId = club_id.into();
        let coll = ClubFormModel::collection(&*self.mdb.clone());
        coll.update_one(
            doc! {
                "club_id":club_id,
                "type": to_bson(&ClubFormType::Join)?,
            },
            doc! {
                "$set": {
                    "type": to_bson(&ClubFormType::Normal)?,
                }
            },
            None,
        )
        .await?;
        Ok(())
    }
    pub async fn update_club_form<T: Into<ObjectId>>(
        &self,
        form_id: T,
        req: UpdateClubFormReq,
    ) -> AppResult<ClubFormModel> {
        let form_id: ObjectId = form_id.into();
        if let Some(ref update_type) = req.form_type {
            if *update_type == ClubFormType::Join {
                let form = ClubFormModel::find_by_id(&*self.mdb.clone(), form_id, None)
                    .await?
                    .ok_or(AppError::ConsError(cons_error::MSG_MDB_NOT_FOUND))?;
                self.reset_join_form(form.club_id).await?;
            }
        }
        if let Some(ref rule) = req.rule {
            Self::valid_form_rule(rule.clone())?;

        }
        let update_doc = Document::from(req);
        let options = FindOneAndUpdateOptions::builder()
            .return_document(ReturnDocument::After)
            .build();
        let res = ClubFormModel::find_one_and_update(
            &*self.mdb.clone(),
            doc! {"_id":form_id},
            UpdateModifications::Document(doc! {"$set":update_doc}),
            Some(options),
        )
        .await?
        .ok_or(AppError::ConsError(cons_error::MSG_MDB_NOT_FOUND))?;
        Ok(res)
    }
    pub async fn form_detail<T: Into<ObjectId>>(&self, id: T) -> AppResult<ClubFormModel> {
        let id: ObjectId = id.into();
        ClubFormModel::find_by_id(&*self.mdb.clone(), id, None)
            .await?
            .ok_or(AppError::ConsError(cons_error::MSG_MDB_NOT_FOUND))
    }
    pub async fn application_detail<T: Into<ObjectId>>(
        &self,
        id: T,
    ) -> AppResult<UserApplicationModel> {
        let id: ObjectId = id.into();

        UserApplicationModel::find_by_id(&*self.mdb.clone(), id, None)
            .await?
            .ok_or(AppError::ConsError(cons_error::MSG_MDB_NOT_FOUND))
    }
    pub async fn delete_form<T: Into<ObjectId>>(&self, form_id: T) -> AppResult<bool> {
        let coll = ClubFormModel::collection(&*self.mdb.clone());

        let res = coll
            .update_one(
                doc! {"_id":form_id.into(),"valid":true},
                doc! {"$set":{"valid":false}},
                None,
            )
            .await?;
        Ok(res.modified_count > 0)
    }
    pub async fn find_all_forms<W: Into<Option<Document>>>(
        &self,
        query: &PaginationQuery,
        where_value: W,
    ) -> AppResult<Pagination<ClubFormModel>> {
        let where_value: Option<Document> = where_value.into();
        let filter = where_value.map(|mut d| {
            d.insert("valid", true);
            d
        });

        let res = ClubFormModel::find_and_pagination(self.mdb.clone(), filter, query).await?;
        Ok(res)
    }

    pub async fn find_all_forms_outer<W: Into<Option<Document>>>(
        &self,
        query: &PaginationQuery,
        where_value: W,
    ) -> AppResult<Pagination<ClubFormModel>> {
        let filter: Option<Document> = where_value.into();
        let mut filter = filter.unwrap_or_default();
        filter.insert("valid", true);
        filter.insert("active", true);
        let res = ClubFormModel::find_and_pagination(self.mdb.clone(), filter, query).await?;
        Ok(res)
    }
    /// ## change_form_active
    /// 修改表单active状态
    pub async fn change_form_active<T: Into<ObjectId>, A: Into<bool>>(
        &self,
        form_id: T,
        active: A,
    ) -> AppResult<ClubFormModel> {
        let form_id: ObjectId = form_id.into();
        let active: bool = active.into();
        let res = ClubFormModel::find_one_and_update(
            &*self.mdb.clone(),
            doc! {"_id":form_id},
            doc! {"active":active},
            FindOneAndUpdateOptions::builder()
                .return_document(ReturnDocument::After)
                .build(),
        )
        .await?
        .ok_or(AppError::ConsError(cons_error::MSG_MDB_NOT_FOUND))?;
        Ok(res)
    }

    pub async fn find_all_application<P: Into<PaginationQuery>, W: Into<Option<Document>>>(
        &self,
        page: P,
        where_value: W,
    ) -> AppResult<Pagination<UserApplicationModel>> {
        let page: PaginationQuery = page.into();
        let where_value: Option<Document> = where_value.into();
        UserApplicationModel::find_and_pagination(self.mdb.clone(), where_value, &page).await
    }

    /// ## apply_application_to_form
    /// ~~用户提交表单申请,默认进入草稿状态~~
    /// 用户提交表单申请进入预设好的状态
    pub async fn apply_application_to_form<UT, FT, RT>(
        &self,
        user: UT,
        form: FT,
        req: RT,
    ) -> AppResult<UserApplicationModel>
    where
        UT: Into<ObjectId>,
        FT: Into<ClubFormModel>,
        RT: Into<ApplyApplicationToFormReq>,
    {
        let req: ApplyApplicationToFormReq = req.into();
        let user_id: ObjectId = user.into();
        let form: ClubFormModel = form.into();
        if let Some(value) = form.rule.as_ref() {
            // 如果表单中的value定义不为空，使用value对content进行校验
            valid_form_data(value, &req.content)
                .map_err(|err_content| AppError::Status(StatusCode::BAD_REQUEST, err_content))?;
        }
        if !form.allow_repeat || form.form_type == ClubFormType::Join {
            // 检查重复提交,并且纳新表单默认不允许重复提交
            let application = UserApplicationModel::find_one(
                &*self.mdb,
                doc! {
                    "form_id":form.id,
                    "user_id":user_id,
                    "$or":[
                        {"status.type":ClubFormStatusFlowType::Default},
                        {"status.type":ClubFormStatusFlowType::Archive},
                        {"status.type":ClubFormStatusFlowType::Success}
                    ]
                },
                None,
            )
            .await?;
            if application.is_some() {
                return Err(AppError::ConsError(
                    cons_error::MSG_FORM_NOT_ALLOW_MULTI_APPLY,
                ));
            }
        }

        let mut application_model = UserApplicationModel {
            id: None,
            user_id,
            form_id: form
                .id
                .ok_or(AppError::ConsError(cons_error::MSG_FORM_ID_IS_NOT_EXIST))?,
            club_id: Some(form.club_id),
            source_form: UserApplicationSourceForm::from_form(&form),
            status: form.status_flow.clone(),
            status_history: vec![],
            created_at: DateTime::now(),
            updated_at: DateTime::now(),
            content: to_bson(&req.content)?,
            status_header: form.status_flow,
        };
        application_model.save(&*self.mdb.clone(), None).await?;
        Ok(application_model)
    }

    /// ## public_application
    /// 公开申请，由草稿状态转到首个审批流程状态
    pub async fn public_application<
        AI: Into<ObjectId>,
        FT: Into<Vec<ClubFormStatusFlow>>,
        O: Into<SimpleUserModel>,
    >(
        &self,
        application_id: AI,
        status_flow: FT,
        op: O,
    ) -> AppResult<UserApplicationModel> {
        let status_flow: Vec<ClubFormStatusFlow> = status_flow.into();
        let application_id: ObjectId = application_id.into();
        let start_status = status_flow
            .iter()
            .find_map(|f| match f.flow_type {
                ClubFormStatusFlowType::Draft => Some(f.clone()),
                _ => None,
            })
            .ok_or(AppError::ConsError(
                cons_error::MSG_FORM_NOT_CONTAIN_START_STATUS,
            ))?;
        let mut application =
            UserApplicationModel::find_by_id(&*self.mdb.clone(), application_id, None)
                .await?
                .ok_or(AppError::ConsError(cons_error::MSG_MDB_NOT_FOUND))?;
        let current_status = application.status.ok_or(AppError::ConsError(
            cons_error::MSG_APPLICATION_STATUS_IS_EMPTY,
        ))?;
        application.status = Some(start_status.clone());
        application.status_history.push(ClubFormStatusHistory::new(
            current_status,
            start_status,
            op,
        ));
        application.save(&*self.mdb.clone(), None).await?;
        Ok(application)
    }

    /// ## archive_application
    /// 将申请归档
    pub async fn archive_application<AI: Into<ObjectId>, O: Into<SimpleUserModel>>(
        &self,
        application_id: AI,
        op: O,
    ) -> AppResult<UserApplicationModel> {
        let op: SimpleUserModel = op.into();
        let application_id: ObjectId = application_id.into();
        let mut application =
            UserApplicationModel::find_by_id(&*self.mdb.clone(), application_id, None)
                .await?
                .ok_or(AppError::ConsError(cons_error::MSG_MDB_NOT_FOUND))?;
        let current_status = application.status.ok_or(AppError::ConsError(
            cons_error::MSG_APPLICATION_STATUS_IS_EMPTY,
        ))?;
        let archive_status = ClubFormStatusFlow::default_archive_status();
        application.status = Some(archive_status.clone());
        application.status_history.push(ClubFormStatusHistory::new(
            current_status,
            archive_status,
            op,
        ));
        application.save(&*self.mdb.clone(), None).await?;
        Ok(application)
    }

    pub async fn delete_application<T: Into<ObjectId>, R: Into<DeleteApplicationReq>>(
        &self,
        application_id: T,
        req: R,
    ) -> AppResult<bool> {
        let req: DeleteApplicationReq = req.into();
        let coll = UserApplicationModel::collection(&*self.mdb.clone());
        let del_count = {
            if req.force {
                coll.delete_one(doc! {"_id": application_id.into()}, None)
                    .await?
                    .deleted_count
            } else {
                coll.delete_one(
                    doc! {"_id":application_id.into(),"status.type":ClubFormStatusFlowType::Draft},
                    None,
                )
                .await?
                .deleted_count
            }
        };
        Ok(del_count > 0)
    }

    /// ## change_application_status
    /// 修改申请的历史状态
    pub async fn change_application_status<
        T: Into<ObjectId>,
        U: Into<SimpleUserModel>,
        S: AsRef<str>,
    >(
        &self,
        application_id: T,
        op: U,
        status_id: S,
    ) -> AppResult<UserApplicationModel> {
        let op: SimpleUserModel = op.into();
        let mut model = UserApplicationModel::find_one(
            &*self.mdb.clone(),
            Some(doc! {"_id":application_id.into()}),
            None,
        )
        .await?
        .ok_or(AppError::ConsError(cons_error::MSG_MDB_NOT_FOUND))?;
        let status = model.status.ok_or(AppError::ConsError(
            cons_error::MSG_APPLICATION_STATUS_IS_EMPTY,
        ))?;
        // 找到新状态
        let new_status = {
            if status.flow_type == ClubFormStatusFlowType::Draft {
                model
                    .status_header
                    .clone()
                    .ok_or(AppError::ConsError(cons_error::MSG_UNKNOWN))?
            } else {
                // TODO: 应该从头部进行全局递归查找
                status
                    .children
                    .iter()
                    .find(|item| item.id == status_id.as_ref())
                    .ok_or(AppError::ConsError(
                        cons_error::MSG_APPLICATION_STATUS_NOT_FOUND,
                    ))?
                    .clone()
            }
        };
        // 记录历史信息
        model.status_history.push(ClubFormStatusHistory {
            op_id: op.id,
            op_name: op.nickname,
            op_time: DateTime::now(),
            op_avatar: op.avatar,
            from: status.clone_without_children(),
            to: new_status.clone_without_children(),
        });
        model.status = Some(new_status);
        // 对ms mongodb做兼容处理
        // TODO 讨论是否有必要做出处理
        match model.save(&*self.mdb.clone(), None).await {
            Ok(_) => {}
            Err(e) => match e {
                wither::WitherError::ServerFailedToReturnUpdatedDoc => {}
                _ => {
                    return Err(e.into());
                }
            },
        };

        Ok(model)
    }

    pub async fn get_user_applications<T: Into<ObjectId>, W: Into<Option<Value>>>(
        &self,
        user_id: T,
        where_value: W,
        page_query: &PaginationQuery,
    ) -> AppResult<Pagination<UserApplicationModel>> {
        let user_id: ObjectId = user_id.into();
        let where_value: Option<Value> = where_value.into();
        let mut filter = where_value
            .and_then(|v| build_filter(&v, &COMMON_NO_REGEXP))
            .unwrap_or_default();
        filter.insert("user_id", user_id);
        UserApplicationModel::find_and_pagination(self.mdb.clone(), filter, page_query).await
    }

    /// ## get_user_application_detail
    /// 查询用户信息详情
    pub async fn get_user_application_detail<T: Into<ObjectId>>(
        &self,
        application_id: T,
    ) -> AppResult<UserApplicationModel> {
        let application_id: ObjectId = application_id.into();

        UserApplicationModel::find_by_id(&*self.mdb.clone(), application_id, None)
            .await?
            .ok_or(AppError::ConsError(cons_error::MSG_MDB_NOT_FOUND))
    }
    /// ## 参加社团活动
    pub async fn join_club_activity<T: Into<JoinClubActivitySvrReq>>(
        &self,
        req: T,
    ) -> AppResult<ClubActivityJoinModel> {
        let req: JoinClubActivitySvrReq = req.into();
        // 预检查
        // TODO: 注意，这里有可能存在并发问题。最好使用find_one_and_update来直接create
        let pre_find = ClubActivityJoinModel::find_one(
            &*self.mdb,
            doc! {
                "activity_id":req.activity_id,
                "joined_user":to_bson(&req.joined_user)?
            },
            None,
        )
        .await?;
        if pre_find.is_some() {
            return Err(AppError::no_permission(
                cons_error::MSG_ALREADY_JOIN_ACTIVITY,
            ));
        }

        let activity = self
            .club_service
            .find_one_club_activity(req.activity_id, doc! {"valid":true})
            .await?
            .ok_or_else(|| AppError::not_found(cons_error::MSG_CLUB_ACTIVITY_IS_NOT_FOUND))?;

        // 检查是否需要提交表单
        let content = match (activity.form_id, req.content.clone()) {
            (Some(id), Some(v)) => Some((v, id)),
            (Some(_), None) => {
                return Err(AppError::no_permission(cons_error::MSG_REQUIRE_FORM_DATA));
            }
            (None, Some(_)) => None,
            (None, None) => None,
        };

        let application_id = if let Some((value, id)) = content {
            // 如果需要提交表单则需要走表单提交逻辑
            let form_detail = self.form_detail(id).await?;

            let res = self
                .apply_application_to_form(req.joined_user.id, form_detail, value)
                .await?;
            let application_id = res
                .id
                .ok_or_else(|| AppError::internal_error(cons_error::MSG_UNKNOWN))?;
            let res_status = res
                .status
                .ok_or_else(|| AppError::internal_error(cons_error::MSG_UNKNOWN))?;
            // 提交完成后将表单直接发布而不是停止在草稿阶段
            if let Some(value) = res_status.children.get(0) {
                self.change_application_status(application_id, req.joined_user.clone(), &value.id)
                    .await?;
            } else {
                // 如果没有下一阶段那么应该直接进入resolve阶段，但是现在并没有这种设计，所以这里先弹个info
                // TODO: 没有下一阶段则进入resolve阶段
                log::info!("表单不存在下一个状态，这里需要额外处理");
            }
            Some(application_id)
        } else {
            None
        };
        // 保存参加列表
        let mut model = ClubActivityJoinModel::from(req);

        model.application_id = application_id;

        model.save(&*self.mdb, None).await?;

        Ok(model)
    }
    pub async fn check_club_activity_already_joined<T: Into<ObjectId>, U: Into<ObjectId>>(
        &self,
        activity_id: T,
        user_id: U,
    ) -> AppResult<bool> {
        let res = ClubActivityJoinModel::find_one(
            &*self.mdb,
            doc! {
                "activity_id":activity_id.into(),
                "joined_user.id":user_id.into()
            },
            None,
        )
        .await?;
        Ok(res.is_some())
    }

    pub async fn search_activity_already_joined<
        T: Into<ObjectId>,
        F: Into<Option<Document>>,
        Q: Borrow<PaginationQuery>,
    >(
        &self,
        activity_id: T,
        filter: F,
        query: Q,
    ) -> AppResult<Pagination<ClubActivityJoinModel>> {
        let mut filter: Document = filter.into().unwrap_or_default();
        let activity_id: ObjectId = activity_id.into();
        filter.insert("activity_id", activity_id);
        let res =
            ClubActivityJoinModel::find_and_pagination(self.mdb.clone(), filter, query.borrow())
                .await?;
        Ok(res)
    }
    pub async fn leave_club_activity<T: Into<ObjectId>, U: Into<ObjectId>>(
        &self,
        activity_id: T,
        user_id: U,
    ) -> AppResult<bool> {
        let user_id: ObjectId = user_id.into();
        let model = ClubActivityJoinModel::find_one(
            &*self.mdb,
            doc! {
                "activity_id":activity_id.into(),
                "joined_user.id":user_id
            },
            None,
        )
        .await?
        .ok_or_else(|| AppError::not_found(cons_error::MSG_ARTICLE_IS_NOT_FOUND))?;
        // 如果是提交了表单的加入请求则需要归档表单
        if let Some(application_id) = model.application_id {
            let user_model = self.user_service.user_detail(user_id).await?;
            self.archive_application(application_id, user_model).await?;
        }
        let res = model.delete(&*self.mdb).await?;
        Ok(res.deleted_count > 0)
    }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateFormToClubReq {
    pub club_id: ObjectId,
    #[serde(rename = "type")]
    pub form_type: ClubFormType,
    pub title: String,
    pub description: Option<String>,
    #[serde(default = "Default::default")]
    pub allow_repeat: bool,
    #[serde(default = "Default::default")]
    pub allow_rewrite: bool,
    pub rule: Option<Value>,
    pub layout: Value,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateClubFormReq {
    #[serde(rename = "type")]
    pub form_type: Option<ClubFormType>,
    pub title: Option<String>,
    pub description: Option<String>,
    pub allow_repeat: Option<bool>,
    pub allow_rewrite: Option<bool>,
    pub layout: Option<Value>,
    pub rule: Option<Value>,
    pub status_flow: Option<ClubFormStatusFlow>,
    pub active: Option<bool>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApplyApplicationToFormReq {
    pub content: Value,
}

impl From<Value> for ApplyApplicationToFormReq {
    fn from(v: Value) -> Self {
        Self { content: v }
    }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeleteApplicationReq {
    #[serde(default = "Default::default")]
    pub force: bool,
}

impl ApplyApplicationToFormReq {
    /// ## validate
    /// 使用json schema检查内容是否合法
    pub fn validate(_schema: Value) -> AppResult<()> {
        todo!()
    }
}

impl From<ApplyApplicationToFormReq> for Document {
    fn from(req: ApplyApplicationToFormReq) -> Self {
        let mut res_doc = Document::new();
        res_doc.insert("content", json_to_document(&req.content));
        res_doc
    }
}

impl From<CreateFormToClubReq> for Document {
    fn from(req: CreateFormToClubReq) -> Self {
        let mut res_doc = Document::new();
        res_doc.insert("club_id", req.club_id);
        res_doc.insert("type", req.form_type as i64);
        res_doc.insert("title", req.title);
        if let Some(description) = req.description {
            res_doc.insert("description", description);
        }
        res_doc.insert("allow_repeat", req.allow_repeat);
        res_doc.insert("allow_rewrite", req.allow_rewrite);
        if let Some(rule) = req.rule {
            res_doc.insert("value", json_to_bson(&rule));
        }

        res_doc.insert("updated_at", DateTime::now());
        res_doc
    }
}

impl From<CreateFormToClubReq> for ClubFormModel {
    fn from(req: CreateFormToClubReq) -> Self {
        Self {
            id: None,
            club_id: req.club_id,
            form_type: req.form_type,
            title: req.title,
            description: req.description,
            allow_repeat: req.allow_repeat,
            allow_rewrite: req.allow_rewrite,
            rule: req.rule.as_ref().and_then(json_to_document),
            layout: json_to_document(&req.layout),
            created_at: DateTime::now(),
            updated_at: DateTime::now(),
            status_flow: None,
            valid: true,
            active: true,
        }
    }
}

impl From<UpdateClubFormReq> for Document {
    fn from(req: UpdateClubFormReq) -> Self {
        let mut res_doc = Document::new();
        if let Some(form_type) = req.form_type {
            res_doc.insert("type", form_type as i64);
        }
        if let Some(title) = req.title {
            res_doc.insert("title", title);
        }
        if let Some(description) = req.description {
            res_doc.insert("description", description);
        }
        if let Some(allow_repeat) = req.allow_repeat {
            res_doc.insert("allow_repeat", allow_repeat);
        }
        if let Some(allow_rewrite) = req.allow_rewrite {
            res_doc.insert("allow_rewrite", allow_rewrite);
        }
        if let Some(layout) = req.layout {
            res_doc.insert("layout", json_to_document(&layout));
        }
        if let Some(rule) = req.rule {
            res_doc.insert("rule", json_to_document(&rule));
        }
        if let Some(status_flow) = req.status_flow {
            res_doc.insert("status_flow", &status_flow);
        }
        if let Some(active) = req.active {
            res_doc.insert("active", active);
        }

        res_doc
    }
}
/// ## 加入社团活动请求
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JoinClubActivitySvrReq {
    pub activity_id: ObjectId,
    pub joined_user: ClubActivityJoinedUser,
    pub content: Option<Value>,
}
impl From<JoinClubActivitySvrReq> for ClubActivityJoinModel {
    fn from(req: JoinClubActivitySvrReq) -> Self {
        Self {
            activity_id: req.activity_id,
            joined_user: req.joined_user,
            ..Default::default()
        }
    }
}
